/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package persistencia;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.datatypes.DTCliente;
import util.datatypes.DTFuncion;
import util.datatypes.DTPelicula;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import util.datatypes.DTEntrada;
import util.datatypes.DTReserva;
import util.datatypes.DTTiposEntrada;
import util.datatypes.DTVenta;

/**
 *
 * @author Ernesto
 */
public class Sistema implements IPersistenciaAdmin, IPersistenciaBoleteria, IPersistenciaKiosco {

    //aqui todas las operaciones para persistir datos
    private Connection cnn;
    private static Sistema instance = null;

    public static Sistema getInstance() {
        if (instance == null) {
            instance = new Sistema();
        }
        return instance;
    }

    private Sistema() {
        try {

            Class.forName("com.mysql.jdbc.Driver");

            cnn = DriverManager.getConnection("jdbc:mysql://localhost:3306/SalaCines", "root", "1qazxc");

        } catch (SQLException ex) {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    // <editor-fold defaultstate="collapsed" desc="Métodos Administracion">
    public int guardarClientes(List<DTCliente> pClientes) throws SQLException {

        try {
            CallableStatement cStmt = cnn.prepareCall("{call altaCliente(?, ?, ?)}");


            for (DTCliente dtc : pClientes) {
                cStmt.setInt(1, dtc.getCi());
                cStmt.setString(2, dtc.getNombre());
                //cStmt.setDate(3, (java.sql.Date) dtc.getFechaNacimiento());
                cStmt.setDate(3,new java.sql.Date(dtc.getFechaNacimiento().getTime()));
                cStmt.executeUpdate();

            }
        } catch (SQLException e) {

            throw e;
        }

        return 1;
    }

    public int guardarPeliculas(List<DTPelicula> pPeliculas) throws SQLException {


        try {
            CallableStatement cStmt = cnn.prepareCall("{call altaPelicula(?, ?)}");


            for (DTPelicula dtp : pPeliculas) {
                cStmt.setInt(1, dtp.getId());
                cStmt.setString(2, dtp.getNombre());
                cStmt.executeUpdate();

            }
        } catch (SQLException e) {

            throw e;
        }

        return 1;
    }

    public int guardarFuncion(DTFuncion f, int idPelicula) throws SQLException {

        try {
            CallableStatement cStmt = cnn.prepareCall("{call altaFuncion(?,?,?,?)}");


            cStmt.setDouble(1, f.getHoraInicio());
            cStmt.setDouble(2, f.getHoraFin());
            cStmt.setInt(3, idPelicula);
            cStmt.setInt(4, f.getEntradasDisponibles());
            cStmt.executeUpdate();


        } catch (SQLException e) {

            throw e;
        }

        return 1;
    }

    public List<DTCliente> listarClientes() throws SQLException{
        List<DTCliente> lista = new LinkedList<DTCliente>();
        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarClientes()}");

            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) {

                int id = rs.getInt("ci");
                String nombre = rs.getString("nombre");
                Date nacimiento= rs.getDate("nacimiento");

                lista.add(new DTCliente(id, nombre, nacimiento));
            }


        } catch (SQLException e) {

            throw e;
        }

        return lista;
    }

    public List<DTPelicula> listarPeliculas() throws SQLException {

        List<DTPelicula> lista = new LinkedList<DTPelicula>();
        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarPeliculas()}");

            ResultSet rs = cStmt.executeQuery();
            while (rs.next()) {

                int id = rs.getInt("id");
                String nombre = rs.getString("nombre");

                lista.add(new DTPelicula(id, nombre));
            }


        } catch (SQLException e) {

            throw e;
        }

        return lista;
    }

    public List<DTFuncion> listarFunciones() throws SQLException {
        List<DTFuncion> lista = new LinkedList<DTFuncion>();
        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarFunciones()}");

            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) {

                int id = rs.getInt("id");
                double horaInicio = rs.getDouble("horaInicio");
                double horaFin = rs.getDouble("horaFin");
                int nroDisponibles = rs.getInt("entradasDisponibles");


                lista.add(new DTFuncion(id, horaInicio, horaFin, nroDisponibles));
            }


        } catch (SQLException e) {

            throw e;
        }

        return lista;
    }

    public List<DTFuncion> listarFuncionesDePelicula(int pId) throws SQLException {
        List<DTFuncion> lista = new LinkedList<DTFuncion>();
        try {

            //uso del proc almacenado
            CallableStatement cStmt = cnn.prepareCall("{call listarFuncionesDePelicula(?)}");
            cStmt.setInt(1, pId);

            ResultSet rs = cStmt.executeQuery();
            while (rs.next()) {


                int id = rs.getInt("id");
                double horaInicio = rs.getDouble("horaInicio");
                double horaFin = rs.getDouble("horaFin");
                int nroDisponibles = rs.getInt("entradasDisponibles");


                lista.add(new DTFuncion(id, horaInicio, horaFin, nroDisponibles));
            }


        } catch (SQLException e) {

            throw e;
        }

        return lista;
    }

    public int obtenerCantidadFuncionesDePelicula(int pId) throws SQLException {

        int cantidad = 0;

        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarFuncionesDePelicula(?)}");
            cStmt.setInt(1, pId);

            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) {

                cantidad++;
            }

        } catch (SQLException e) {

            throw e;
        }
        return cantidad;
    }
    public int obtenerCantidadPeliculas() throws SQLException{

        int cantidad = 0;

        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarPeliculas()}");

            ResultSet rs = cStmt.executeQuery();
            while (rs.next()) {

                cantidad++;
            }

        } catch (SQLException e) {

            throw e;
        }

        return cantidad;
    }
    public int obtenerCantidadClientes() throws SQLException{

        int cantidad = 0;

        try {

            CallableStatement cStmt = cnn.prepareCall("{call listarClientes()}");

            ResultSet rs = cStmt.executeQuery();
            while (rs.next()) {

                cantidad++;
            }


        } catch (SQLException e) {

            throw e;
        }

        return cantidad;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Métodos TiposEntrada">
    public List<DTTiposEntrada> getTiposEntradas ()
    {
        List<DTTiposEntrada> listaTiposEntradas = new LinkedList<DTTiposEntrada>();

        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call listarTiposEntradas()}");
            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) 
            {
                listaTiposEntradas.add(new DTTiposEntrada(rs.getInt("id"), 0, rs.getString("nombre"), rs.getDouble("precio")));
            }
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
        return listaTiposEntradas;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Métodos Reservas">
    public void agregarReserva(int ci, DTReserva res) throws SQLException {
        try {

            PreparedStatement pst = cnn.prepareStatement("INSERT INTO reservas (ci, fecha, costoTotal, estado, vendida, idFuncion) VALUES (?, ?, ?, ?, ?, ?)");

            pst.setInt(1, ci);
            pst.setTimestamp(2, new Timestamp(res.getFecha().getTime()));
            pst.setDouble(3, res.getCostoTotal());
            pst.setBoolean(4, false);
            pst.setBoolean(5, false);
            pst.setInt(6, res.getFuncion().getId());

            pst.executeUpdate();

            int idReserva = ultimoIdReserva();

            for (DTTiposEntrada dt : res.getTiposEntradas()) {

                pst.clearBatch();
                pst.clearParameters();

                pst = cnn.prepareStatement("INSERT INTO reservaTipoEntrada (idTipoEntrada, idReserva, cantidad) VALUES (?, ?, ?)");

                pst.setInt(1, dt.getId());
                pst.setInt(2, idReserva);
                pst.setInt(3, dt.getCantidadEntradas());

                pst.executeUpdate();
            }

            pst.close();
        } catch (Exception e) {
            e.getMessage();
        }

    }

    private int ultimoIdReserva(){
        try {

            PreparedStatement pst = cnn.prepareStatement("SELECT last_insert_id()");
            ResultSet rs = pst.executeQuery();

            if (rs.next()){

                return rs.getInt(1);
            }

        } catch (SQLException ex) {

            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }

        return 0;
    }


    public void cancelarReserva(int idReserva)
    {
        try
        {
            PreparedStatement pst = cnn.prepareStatement("UPDATE reservas SET estado = 1 WHERE id = ? ");

            pst.setInt(1, idReserva);
            pst.executeUpdate();
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void modificarReserva(DTReserva r)
    {
        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call modificarReserva(?,?,?,?,?,?)}");
            cStmt.setInt(1, r.getId());
            Timestamp time = new Timestamp(Calendar.getInstance().getTime().getTime());
            cStmt.setTimestamp(2, time);
            cStmt.setDouble(3, r.getCostoTotal());
            cStmt.setBoolean(4, r.isEstado());
            cStmt.setBoolean(5, r.isVendida());
            cStmt.setInt(6, r.getFuncion().getId());
            cStmt.executeQuery();
            modificarTiposEntardaReserva(r);
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private void modificarTiposEntardaReserva(DTReserva r)
    {
        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call modificarTiposEntradaReserva(?,?,?)}");
            for(DTTiposEntrada te: r.getTiposEntradas())
            {
                cStmt.setInt(1, r.getId());
                cStmt.setInt(2, te.getId());
                cStmt.setInt(3, te.getCantidadEntradas());
                cStmt.executeQuery();
            }
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public List<DTReserva> listarReservas (int ciCliente)
    {
        List<DTReserva> listaReservas = new LinkedList<DTReserva>();

        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call listarReservas(?)}");
            cStmt.setInt(1, ciCliente);
            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) 
            {
                DTFuncion funcion = new DTFuncion(rs.getInt("idFuncion"), rs.getDouble("horaInicio"), rs.getDouble("horaFin"), rs.getInt("entradasDisponibles"));
                DTReserva reserva = new DTReserva(rs.getInt("id"), rs.getTimestamp("fecha"), rs.getFloat("costoTotal"), rs.getBoolean("estado"),
                        rs.getBoolean("vendida"), funcion, null);

                reserva.setTiposEntradas(listarTiposReservas(reserva.getId()));
                listaReservas.add(reserva);
            }
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
        return listaReservas;
    }
    
    private List<DTTiposEntrada> listarTiposReservas(int idReserva)
    {
        List<DTTiposEntrada> listaTipos= new LinkedList<DTTiposEntrada>();

        try
        {
            CallableStatement csRes = cnn.prepareCall("{call listarTiposEntradasReserva (?)}");
            csRes.setInt(1, idReserva);
            ResultSet rsRes = csRes.executeQuery();

            while (rsRes.next())
            {
                listaTipos.add(new DTTiposEntrada(rsRes.getInt("idTipoEntrada"), rsRes.getInt("cantidad"), rsRes.getString("nombre"), rsRes.getDouble("precio")));
            }

        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }

        return listaTipos;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Métodos Venta">
    public void almacenarVentas(DTVenta venta) throws SQLException
    {
        try 
        {
            PreparedStatement pst = cnn.prepareStatement("INSERT INTO ventas (idReserva, valorAbonado) VALUES(?,?)");
            pst.setInt(1, venta.getReserva().getId());
            pst.setDouble(2, venta.getValorAbonado());
          
            pst.executeUpdate();

        } catch (SQLException ex) {

            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void almacenarEntradas (List<DTEntrada> entradas, int idVenta) throws SQLException
    {
        try 
        {
            PreparedStatement pst = cnn.prepareStatement("INSERT INTO entradas (fecha, idVenta, idFuncion, idTipoEntrada) VALUES(?,?,?,?)");

            for (DTEntrada e: entradas)
            {
                Timestamp time = new Timestamp(e.getFecha().getTime());
                pst.setTimestamp(1, time);
                pst.setInt(2, idVenta);
                pst.setInt(3, e.getFuncion().getId());
                pst.setInt(4, e.getTipo().getId());
                pst.executeUpdate();

            }
        } catch (SQLException ex) {

            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public List<DTVenta> getVentas()
    {
        List<DTVenta> listaVentas = new LinkedList<DTVenta>();

        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call listarVentas()}");
            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) 
            {
                DTFuncion funcion = new DTFuncion(rs.getInt("idFuncion"), rs.getDouble("horaInicio"), rs.getDouble("horaFin"), rs.getInt("entradasDisponibles"));
                DTReserva reserva = new DTReserva(rs.getInt("idReserva"), rs.getTimestamp("fecha"), rs.getDouble("costoTotal"), rs.getBoolean("estado"), rs.getBoolean("vendida"), funcion, null);
                reserva.setTiposEntradas(listarTiposReservas(reserva.getId()));
                listaVentas.add(new DTVenta(rs.getInt("idVenta"), rs.getDouble("valorAbonado"), reserva));
            }
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }

        return listaVentas;
    }
    
    public List<DTEntrada> getEntradas(int idVenta)
    {
        List<DTEntrada> listaEntradas = new LinkedList<DTEntrada>();

        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call listarEntradas(?)}");
            cStmt.setInt(1, idVenta);
            ResultSet rs = cStmt.executeQuery();

            while (rs.next()) {
                DTFuncion funcion = new DTFuncion(rs.getInt("idFuncion"), rs.getDouble("horaInicio"), rs.getDouble("horaFin"), rs.getInt("entradasDisponibles"));
                DTTiposEntrada tipo = new DTTiposEntrada(1, rs.getString("nombre"), rs.getDouble("precio"));
                DTEntrada entrada = new DTEntrada(rs.getInt("idEntrada"), tipo, rs.getTimestamp("fecha"));
                entrada.setFuncion(funcion);
                listaEntradas.add(entrada);
            }
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }

        return listaEntradas;
    }

    public void venderReserva(int idReserva)
    {
        try
        {
            CallableStatement cStmt = cnn.prepareCall("{call venderReserva(?)}");
            cStmt.setInt(1, idReserva);
            cStmt.executeQuery();
        }
        catch(SQLException ex)
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public int getCantidadEntradasVendidas (Calendar desde, Calendar hasta, int idFuncion)
    {
        int cantidad = 0;
        
        try 
        {
            CallableStatement pst = cnn.prepareCall("SELECT COUNT(e.id) FROM entradas e INNER JOIN funciones f ON e.idFuncion = f.id WHERE idFuncion = ? AND fecha BETWEEN ? AND ?");
            pst.setInt(1, idFuncion);
            pst.setTimestamp(2, new Timestamp(desde.getTime().getTime()));
            pst.setTimestamp(3, new Timestamp(hasta.getTime().getTime()));
            ResultSet rs = pst.executeQuery();

            while (rs.next()) {
                cantidad = rs.getInt(1);
            }
        } 
        catch (SQLException ex) 
        {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
        return cantidad;
    }
    // </editor-fold>
}
